﻿using Cache.Cache;
using Cache.Cache.DistributedCache;
using Cache.Cache.MemoryCache;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;

namespace Cache.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly IMemoryCache _memoryCache;
        private readonly ILogger<TestController> _logger;
        private readonly IMemoryCacheHelper memoryCacheHelper;
        private readonly IDistributedCache disCache;
        private readonly IDistributedCacheHelper distributedCacheHelper;

        public TestController(IMemoryCache memoryCache, IMemoryCacheHelper memoryCacheHelper,
            ILogger<TestController> logger, IDistributedCache disCache, IDistributedCacheHelper distributedCacheHelper)
        {
            this.memoryCacheHelper = memoryCacheHelper;
            _memoryCache = memoryCache;
            _logger = logger;
            this.disCache = disCache;
            this.distributedCacheHelper = distributedCacheHelper;
        }

        #region 内存缓存 GetOrCreateAsync
        [HttpGet]
        public async Task<ActionResult<Book>> GetBookById(long id)
        {
            _logger.LogInformation($"开始执行GetBookById,id={id}");
            //GetOrCreateAsync:先获取缓存,缓存不存在则在委托中获取数据和创建缓存
            Book? book = await _memoryCache.GetOrCreateAsync<Book?>("Book_" + id, async a =>
            {
                //设置缓存过期时间相较于当前时间10秒后过期
                //a.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
                //a.SlidingExpiration = TimeSpan.FromSeconds(10);

                //a.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
                //a.SlidingExpiration = TimeSpan.FromSeconds(10);

                //Random.Shared:线程安全的随机实例
                //缓存属于高频率调用,不建议new Random()
                //缓存过期时间随机值
                a.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(Random.Shared.Next(10, 15));

                _logger.LogInformation($"缓存里没找到,到数据库中查一查,id={id}");
                return await MyDbContext.GetByIdAsync(id); //即使查出来的值为null,也会保存在缓存里
            });

            _logger.LogInformation("执行结束:{0}", book == null ? "为空" : book);


            //_memoryCache.GetOrCreateAsync("Book_" + id, async _ => { //如果不需要ICacheEntry这个参数可以使用弃元:_
            //    return await MyDbContext.GetByIdAsync(id);
            //});

            //Book? book = await _memoryCache.GetOrCreateAsync<Book?>("Book_" + id, async (a) =>
            //{
            //    return await MyDbContext.GetByIdAsync(id);
            //});

            _logger.LogInformation($"GetOrCreateAsync结果:{book}");
            if (book == null)
            {
                return NotFound($"找不到id={id}的书");
            }
            else
            {
                return book;
            }

        }
        #endregion

        #region asp.net core 响应缓存
        [HttpGet]
        [ResponseCache(Duration = 20)]
        public DateTime Now()
        {
            return DateTime.Now;
        }
        #endregion

        #region 自定义内存缓存封装MemoryCacheHelper
        [HttpPost]
        public string TestGetCacheKey()
        {
            return this.CalcCacheKeyFromAction();
        }

        [HttpPost]
        public double Test()
        {
            //MemoryCacheHelper.ValidateValueType<IEnumerable<string>>();
            return Random.Shared.NextDouble(1, 10);
        }

        /// <summary>
        /// 测试自己封装的本地缓存封装类
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult<Book?>> TestCustomerMemoryCache([FromBody] int id)
        {
            Book? book = await memoryCacheHelper.GetOrCreateAsync<Book>("book" + id, async a =>
            {
                return await MyDbContext.GetByIdAsync(id);
            }, 120);

            if (book == null)
            {
                return NotFound("不存在");
            }

            return book;
        }

        /// <summary>
        /// 测试自己封装的本地缓存封装类
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult<Book?> TestCustomerMemoryCache2([FromBody] int id)
        {
            Book? book = memoryCacheHelper.GetOrCreate<Book>("book" + id, a =>
            {
                return MyDbContext.GetById(id);
            }, 120);

            if (book == null)
            {
                return NotFound("不存在");
            }

            return book;
        }

        #endregion

        #region 分布式缓存
        [HttpGet]
        public async Task<ActionResult<Book>> GetBookByIdDistributedCahce(long id)
        {
            Book? book;

            string? s = await disCache.GetStringAsync("Book" + id);
            if (s == null)
            {
                book = await MyDbContext.GetByIdAsync(id);
                //如果数据库查询出来的book是null,也会被序列化后存到redis中
                //比如id=666,不存在的数据,book为null,序列化后存到了redis,从而也解决了缓存穿透的问题
                await disCache.SetStringAsync("Book" + id, JsonSerializer.Serialize(book));
            }
            else
            {
                book = JsonSerializer.Deserialize<Book?>(s);
            }

            if (book == null)
            {
                return NotFound("不存在");
            }
            else
            {
                return book;
            }
        }
        #endregion

        #region 自定义分布式缓存
        [HttpGet]
        public async Task<ActionResult<Book?>> GetBookByIdCustomerDistributedCahce(long id)
        {
            Book? book = await distributedCacheHelper.GetOrCreateAsync("Book" + id, async (e) =>
            {
                //如果还想采用滑动过期时间,在这里设置即可
                e.SlidingExpiration = TimeSpan.FromSeconds(5);
                var book = await MyDbContext.GetByIdAsync(id);
                return book;
            });

            if (book == null)
            {
                return NotFound("不存在");
            }
            else
            {
                return book;
            }
        }

        [HttpGet]
        public async Task<int> Test5(long id)
        {
             //存整数这类基本类型看看效果
             return await distributedCacheHelper.GetOrCreateAsync<int>("Book" + id, async (e) =>
             {
                 return 1;
             });
        }
        #endregion

    }
}
